Conversation
Co-authored-by: Copilot <copilot@github.com>
Coverage |
There was a problem hiding this comment.
Pull request overview
This PR fixes GitLab branch protection behavior for MergeOnly branch rules by recreating protected-branch rules when switching to “no one can push”, and ensures branch-protection failures are surfaced to callers.
Changes:
- Recreate protected-branch rules (unprotect + protect) when enforcing merge-only (“no push”) access levels to avoid stale GitLab permissions.
- Return an error from
syncConfiguredBrancheswhen branch protection fails (instead of only logging). - Add contract tests covering merge-only rule recreation and error propagation.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
gitlab/protect.go |
Adds recreateProtectedBranch and uses it to enforce merge-only rules reliably. |
gitlab/protect_contract_test.go |
Adds a contract test asserting merge-only updates recreate (DELETE+POST) rather than PATCH. |
gitlab/branches.go |
Propagates protection failures from syncConfiguredBranches via a wrapped error. |
gitlab/branches_contract_test.go |
Adds a contract test ensuring syncConfiguredBranches returns an error when protection fails. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func TestSyncConfiguredBranches_ReturnsErrorWhenProtectionFails(t *testing.T) { | ||
| client := newContractClient(t, func(w http.ResponseWriter, r *http.Request) { | ||
| switch { | ||
| case r.Method == http.MethodPatch && r.URL.Path == "/api/v4/projects/1": | ||
| _, _ = w.Write([]byte(`{"id":1,"name":"repo"}`)) | ||
| case r.Method == http.MethodGet && strings.Contains(r.URL.Path, "/api/v4/projects/1/protected_branches/main"): | ||
| _, _ = w.Write([]byte(`{"id":1,"name":"main","push_access_levels":[{"id":10,"access_level":40}],"merge_access_levels":[{"id":11,"access_level":40}],"unprotect_access_levels":[{"id":12,"access_level":40}]}`)) | ||
| case r.Method == http.MethodDelete && strings.Contains(r.URL.Path, "/api/v4/projects/1/protected_branches/main"): | ||
| w.WriteHeader(http.StatusNoContent) | ||
| case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/api/v4/projects/1/protected_branches"): | ||
| w.WriteHeader(http.StatusInternalServerError) | ||
| _, _ = w.Write([]byte(`{"message":"500 Internal Server Error"}`)) | ||
| default: | ||
| w.WriteHeader(http.StatusNotFound) | ||
| } | ||
| }) | ||
|
|
||
| cfg := &config.AssignmentConfig{ | ||
| Branches: []config.BranchRule{{Name: "main", MergeOnly: true, Default: true}}, | ||
| } | ||
| project := &gitlabapi.Project{ID: 1, Name: "repo"} | ||
|
|
||
| err := client.syncConfiguredBranches(cfg, project, "main") | ||
| if err == nil { | ||
| t.Fatal("syncConfiguredBranches() expected error when branch protection fails, got nil") | ||
| } |
There was a problem hiding this comment.
This test only asserts err != nil, so it can pass even if syncConfiguredBranches fails earlier (e.g., default-branch update) and never reaches the branch-protection code path you intend to validate. Tighten the assertion to ensure the error is coming from the protection step (e.g., check that the error message includes the new "error while protecting configured branches" wrapper and/or that the POST /protected_branches endpoint was actually hit).
| case r.Method == http.MethodPatch && r.URL.Path == "/api/v4/projects/1": | ||
| _, _ = w.Write([]byte(`{"id":1,"name":"repo"}`)) | ||
| case r.Method == http.MethodGet && strings.Contains(r.URL.Path, "/api/v4/projects/1/protected_branches/main"): |
There was a problem hiding this comment.
The mock server currently only treats PATCH /api/v4/projects/1 as a success. Since this test’s goal is to reach the protection step, consider making this handler accept whatever HTTP method Projects.EditProject actually uses (or at least fail the test if an unexpected method hits this endpoint) so the test can’t silently pass/fail due to a mock mismatch.
| // GitLab can keep stale push permissions when updating existing rules to | ||
| // "No one" (merge-only). Recreate the rule to enforce the access levels. | ||
| if pushAccessLevel == gitlab.NoPermissions { | ||
| if err := c.recreateProtectedBranch(project, branch, pushAccessLevel, mergeAccessLevel); err != nil { | ||
| return err | ||
| } | ||
| return nil | ||
| } |
There was a problem hiding this comment.
In the merge-only path, protectSingleBranch always unprotects and re-protects whenever pushAccessLevel == gitlab.NoPermissions, even if the existing protected-branch rule is already correctly configured. This introduces a small window where the branch is briefly unprotected (race risk) and adds extra API calls. Consider only recreating when the existing rule’s push access levels don’t already represent “no one”, or try an update first and fall back to recreate only if GitLab leaves stale push permissions.
| if err := c.recreateProtectedBranch(project, branch, pushAccessLevel, mergeAccessLevel); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil |
There was a problem hiding this comment.
On the GET-404 path, protectSingleBranch calls recreateProtectedBranch, which performs a DELETE (unprotect) before POST (protect). When the branch isn’t protected yet, this DELETE is unnecessary and adds an extra API round-trip. Consider calling ProtectRepositoryBranches directly in the not-found case and reserving the delete+recreate logic for the “existing rule but needs reset” scenario.
…#60) * feat: implement merge request approval rules and settings - Added functionality to apply merge request approval rules based on member count. - Introduced methods to handle approval settings for merge requests. - Enhanced branch protection logic to consider approval rules. - Updated tests to cover new approval rule functionalities and edge cases. - Refactored existing branch protection methods to accommodate new logic. Co-authored-by: Copilot <copilot@github.com> * fix: branch protection for mergeOnly (#61) Co-authored-by: Copilot <copilot@github.com> * feat: implement merge request approval rules and settings - Added functionality to apply merge request approval rules based on member count. - Introduced methods to handle approval settings for merge requests. - Enhanced branch protection logic to consider approval rules. - Updated tests to cover new approval rule functionalities and edge cases. - Refactored existing branch protection methods to accommodate new logic. Co-authored-by: Copilot <copilot@github.com> * fix: add missing parameter to syncConfiguredBranches call in test * refactor: go install v2 (#62) * feat: implement merge request approval rules and settings - Added functionality to apply merge request approval rules based on member count. - Introduced methods to handle approval settings for merge requests. - Enhanced branch protection logic to consider approval rules. - Updated tests to cover new approval rule functionalities and edge cases. - Refactored existing branch protection methods to accommodate new logic. Co-authored-by: Copilot <copilot@github.com> * feat: implement merge request approval rules and settings - Added functionality to apply merge request approval rules based on member count. - Introduced methods to handle approval settings for merge requests. - Enhanced branch protection logic to consider approval rules. - Updated tests to cover new approval rule functionalities and edge cases. - Refactored existing branch protection methods to accommodate new logic. Co-authored-by: Copilot <copilot@github.com> * fix: add missing parameter to syncConfiguredBranches call in test * refactor: update import paths to use v2 for consistency across the project Co-authored-by: Copilot <copilot@github.com> --------- Co-authored-by: Copilot <copilot@github.com> * fix: update module path to v2 for go install support * fix: enhance version command to display build information conditionally (#63) * fix: update troubleshooting documentation and improve approval rule handling for multiple branches Co-authored-by: Copilot <copilot@github.com> * docs: add warning about project-wide squash settings in approval rules Co-authored-by: Copilot <copilot@github.com> * Update gitlab/branches.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix: refine error message check for any-approver rule in merge request approval tests Co-authored-by: Copilot <copilot@github.com> --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
No description provided.